
///这个目录负责游戏线程与网络线程的沟通。是一个空值线程。
///整个项目设计为异步事件循环的处理方式，总共有3个线程：主线程（负责游戏线程与网络线程的沟通）、
///游戏线程（只负责处理游戏逻辑，从主线程取消息并处理，返回消息给主线程）、
///网络线程（负责接收网络消息，并将消息传递给主线程；负责发送消息给客户端）。
/// (今后可能还要增加一个数据库服务线程，4个线程，很和谐...)。
///
///其实这个设计好处是：客户端连接统一管理，可以很方便的统一推送消息；缺点是连接统一管理，可能消息
///处理不及时，线程模型确定，想要扩展线程就比较困难。
///
///另外还有一种设计是：由游戏房间来管理用户连接，把每个游戏房间作为一个线程任务，然后循环房间列表，
///交由线程池处理，可以很方便地扩展线程。循环房间列表时，有消息处理，则处理消息，没消息时，可以发
/// 送心跳包，节省资源（这种方式的前提是，TcpStream实现了Send，可以通过Channel传送。似乎只有mio的
/// stream实现了Send）。
///
///先采用这种简陋的设计将业务实现，以后有空了可以再实现另一种。
/// 一般来说，将一个流程拆分成几个步骤再多线程处理，不是最好的方式。因为这样容易出现瓶颈。
/// 最好是将流程封装成单独的任务，多线程处理。

///
/// 玩家客户端消息结构：
/// {"code":"", "body":{}}
/// code作为消息类型唯一标识，区分消息处理方式
///

///消息处理部分比较复杂，还需要好好思考如何处理，保持优雅的代码实现。

use std::sync::mpsc;
use std::sync::mpsc::{SyncSender, Receiver};
use std::collections::HashMap;

use game;
use net;

mod chess;
mod player;
mod queue;

///
/// 暂时没考虑服务停止的问题。
/// 可以实现安全停止服务：
/// 1. game_loop和net_loop返回handler;
/// 2.设置一个run标记值;
/// 3.收到停止服务消息时，置run标记为false;
/// 4.主线程判断run为false时跳出loop;
/// 5.向子线程发送停止服务消息;
/// 6.子线程收到消息后，停止接受新的连接，并继续服务现有连接;
/// 7.当子线程服务完现有连接后，线程返回;
/// 8.主线程等待线程返回后，主线程结束。
///
pub fn run(){
    let (pr_send, pr_recv) = mpsc::sync_channel(10);
    let (cc_send, cc_recv) = mpsc::sync_channel(10);
    game::game_loop(pr_send, cc_recv);

    let (gm_send, gm_recv) = mpsc::sync_channel(10);
    let (nm_send, nm_recv) = mpsc::sync_channel(10);
    net::net_loop(gm_send, nm_recv);

    //需要两个映射：用户id到channel id的映射，和channel id到用户id的映射。典型的空间换时间。
    let mut map = Mapping::new();
    let mut queue = queue::Queue::with_capacity(16);

    info!("Control thread running..........");
    loop{
        player::receive_from_net(&gm_recv, &cc_send, &nm_send, &mut map, &mut queue);
        chess::receive_from_game(&pr_recv, &nm_send, &mut map);
    }
}

///玩家id和玩家连接id的映射
pub struct Mapping{
    ///玩家到网络连接的映射 user to channel
    utc: HashMap<u32, u32>,

    ///网络连接到玩家的映射 channel to user
    ctu: HashMap<u32, u32>,

    ///玩家到游戏房间id的映射 user to room
    utr: HashMap<u32, u32>,
}

impl Mapping{
    pub fn new() -> Mapping{
        let utc = HashMap::<u32, u32>::new();
        let ctu = HashMap::<u32, u32>::new();
        let utr = HashMap::<u32, u32>::new();
        Mapping{
            utc,
            ctu,
            utr
        }
    }

    ///插入一条用户连接信息。用户接入和用户加入游戏房间是两个操作步骤。
    pub fn put_channel(&mut self, user: u32, channel: u32){
        self.utc.insert(user, channel);
        self.ctu.insert(channel, user);
    }

    ///保存游戏房间信息。主要是保存用户id到房间id的映射。
    pub fn put_room(&mut self, black: u32, white: u32, room: u32){
        self.utr.insert(black, room);
        self.utr.insert(white, room);
    }

    ///移除一条用户信息，同时要移除用户连接信息，和房间映射信息。
    pub fn remove_user(&mut self, user: &u32){
        let ret = self.utc.remove(user);
        if let Some(c) = ret{
            self.ctu.remove(&c);
        }
        self.utr.remove(user);
    }

    ///移除用户连接信息。同时要移除用户到房间的映射。
    pub fn remove_channel(&mut self, channel: &u32){
        let ret = self.ctu.remove(channel);
        if let Some(u) = ret{
            self.utc.remove(&u);
            self.utr.remove(&u);
        }
    }

    ///根据用户id获取连接id
    pub fn get_channel(&self, user: &u32) -> Option<&u32>{
        self.utc.get(user)
    }

    ///根据连接id获取用户id
    pub fn get_user(&self, channel: &u32) -> Option<&u32>{
        self.ctu.get(channel)
    }

    ///根据用户id获取用户所在的房间id
    pub fn get_room(&self, user: &u32) -> Option<&u32>{
        self.utr.get(user)
    }
}